/**
 * AjLog - The AspectJ based logging facade
 *
 * Copyright (C) 2009 - 2010, Marko Umek. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ajlog.core.logging;

import java.util.HashMap;
import java.util.Map;

import org.ajlog.annotations.levels.EAjLogLevel;
import org.ajlog.loglog.ILogger;
import org.ajlog.loglog.LoggerFactory;
import org.ajlog.util.AjLogAssert;

/**
 * AjLogLevelMapper associates any {@link EAjLogLevel} with the appropriate
 * {@link ILogMethod} provided by the underlying {@link ILoggingFramework}.
 * 
 * @author Marko Umek
 * 
 */
public final class AjLogLevelMapper<LOGGER extends Object>
    implements IAjLogLevelMapper {
    private final static ILogger               internalLogger = LoggerFactory.getLogger(AjLogLevelMapper.class);
    private final Map<EAjLogLevel, ILogMethod> logLevelMap    = new HashMap<EAjLogLevel, ILogMethod>();
    private boolean                            validated      = false;

    /**
     * Constructor
     */
    public AjLogLevelMapper() {
    }

    /**
     * addLogMethodPrototype associates a <code>logMethod</code> prototype to
     * the <code>logLevel</code>.
     * 
     * @param logLevel
     *            the log level
     * @param logMethod
     *            the logging method
     */
    public void addLogMethodPrototype(final EAjLogLevel logLevel, final AbstractLogMethod<LOGGER> logMethod) {
        if (this.validated) {
            AjLogAssert.fail("addLogMethodPrototype must not be called after validate has been executed");
        }
        AjLogAssert.notNull(logLevel, "filterLevel must not be null");
        AjLogAssert.notNull(logMethod, "logMethod must not be null");
        internalLogger.info("Associate loglevel (%s) with log method prototyp (%s)", logLevel, logMethod.getClass());
        if (null != this.logLevelMap.put(logLevel, logMethod)) {
            AjLogAssert.fail("log level=" + logLevel + " already set. Override is not permitted");
        }
    }

    /**
     * 
     * Convenient method for associate a single log method prototype to one or
     * more filter levels.
     * 
     * @param logMethod
     *            the logging method
     * @param logLevels
     *            the log levels
     */
    public void addLogMethodPrototypes(final AbstractLogMethod<LOGGER> logMethod, final EAjLogLevel... logLevels) {
        if (logLevels.length == 0) {
            internalLogger.warning("A logging method should be assicated at least with one filter level");
        }
        for (final EAjLogLevel filterLevel : logLevels) {
            this.addLogMethodPrototype(filterLevel, logMethod);
        }
    }

    /* (non-Javadoc)
     * @see org.ajlog.core.logging.IAjLogLevelMapper#validate()
     */
    public void validate() {
        int failed = 0;
        for (final EAjLogLevel level : EAjLogLevel.values()) {
            internalLogger.debug("Check Filter Level (%s)", level);
            if (!this.logLevelMap.containsKey(level)) {
                internalLogger.fatal("Filter Level (%s) has no associated logging method", level);
                failed++;
            }
        }

        if (failed > 0) {
            AjLogAssert.fail(failed + " filter level(s) have not been associated with a log method");
        }

        this.validated = true;
    }

    /* (non-Javadoc)
     * @see org.ajlog.core.logging.IAjLogLevelMapper#createLogMethod(org.ajlog.annotations.levels.EAjLogLevel, java.lang.Object)
     */
    public ILogMethod createLogMethod(final EAjLogLevel logLevel, final Object loggingInstance) {
        if (!this.validated) {
            AjLogAssert.fail("Internal error: validate must be called before creating any log method instances");
        }
        final ILogMethod logMethodPrototype = this.logLevelMap.get(logLevel);
        AjLogAssert
                .notNull(logMethodPrototype, "logMethod prototype must not be null (Oops. validate() does not work correct)");

        final ILogMethod logMethodInstance = logMethodPrototype.createCopy(loggingInstance);
        AjLogAssert
                .notNull(logMethodInstance,
                        "logMethod instance must not be null (ILogMethod.createCopy is not implemented correctly)");

        if (!logMethodInstance.isValid()) {
            AjLogAssert.fail("Internal error: the log method instance is invalid.");
        }

        return logMethodInstance;
    }

}
